﻿using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using AutoTest.Common.Extension;

namespace AutoTest.Common.UtilsHelper
{
    /// <summary>
    /// 加密帮助类
    /// </summary>
    public static class EncryptHelper
    {

        #region RSA

        /// <summary>
        /// 最终提交请求时，需对敏感信息加密，如身份证、银行卡号。
        /// 加密算法是RSA，使用从接口下载到的公钥进行加密，非后台下载到的私钥。
        /// 
        /// </summary>
        /// <param name="text">要加密的明文</param>
        /// <param name="publicKey"> -----BEGIN CERTIFICATE----- 开头的string，转为bytes </param>
        /// <returns></returns>
        public static string RSAEncrypt(string text, byte[] publicKey,bool fOAEP= false)
        {
            using var x509 = new X509Certificate2(publicKey);
            using var rsa = (RSACryptoServiceProvider)x509.PublicKey.GetRSAPublicKey()!;
            var buff = rsa?.Encrypt(Encoding.UTF8.GetBytes(text), fOAEP)??[];
            return Convert.ToBase64String(buff);
        }

        #endregion

        #region MD5加密

        /// <summary>
        /// MD5加密
        /// </summary>
        public static string Md532(string value)
        {
            ArgumentNullException.ThrowIfNull(value);

            var encoding = Encoding.UTF8;
            MD5 md5 = MD5.Create();
            return HashAlgorithmBase(md5, value, encoding);
        }

        /// <summary>
        /// 加权MD5加密
        /// </summary>
        public static string Md532(string value, string salt)
        {
            return salt.IsEmpty() ? Md532(value) : Md532(value + "『" + salt + "』");
        }

        #endregion

        #region SHA 加密

        /// <summary>
        /// SHA1 加密
        /// </summary>
        public static string Sha1(this string value)
        {
            ArgumentNullException.ThrowIfNull(value);

            var encoding = Encoding.UTF8;
            SHA1 sha1 = SHA1.Create(); //new SHA1CryptoServiceProvider();
            return HashAlgorithmBase(sha1, value, encoding);
        }

        /// <summary>
        /// SHA256 加密
        /// </summary>
        public static string Sha256(this string value)
        {
            ArgumentNullException.ThrowIfNull(value);
            var encoding = Encoding.UTF8;
            SHA256 sha256 = SHA256.Create(); //new SHA256Managed();
            return HashAlgorithmBase(sha256, value, encoding);
        }

        /// <summary>
        /// SHA512 加密
        /// </summary>
        public static string Sha512(this string value)
        {
            ArgumentNullException.ThrowIfNull(value);
            var encoding = Encoding.UTF8;
            SHA512 sha512 = SHA512.Create(); //new SHA512Managed();
            return HashAlgorithmBase(sha512, value, encoding);
        }

        #endregion

        #region HMAC 加密

        /// <summary>
        /// HmacSha1 加密
        /// </summary>
        public static string HmacSha1(this string value, string keyVal)
        {
            ArgumentNullException.ThrowIfNull(value);
            var encoding = Encoding.UTF8;
            var keyStr = encoding.GetBytes(keyVal);
            var hmacSha1 = new HMACSHA1(keyStr);
            return HashAlgorithmBase(hmacSha1, value, encoding);
        }

        /// <summary>
        /// HmacSha256 加密
        /// </summary>
        public static string HmacSha256(this string value, string keyVal)
        {
            ArgumentNullException.ThrowIfNull(value);
            var encoding = Encoding.UTF8;
            byte[] keyStr = encoding.GetBytes(keyVal);
            var hmacSha256 = new HMACSHA256(keyStr);
            return HashAlgorithmBase(hmacSha256, value, encoding);
        }

        /// <summary>
        /// HmacSha384 加密
        /// </summary>
        public static string HmacSha384(this string value, string keyVal)
        {
            ArgumentNullException.ThrowIfNull(value);
            var encoding = Encoding.UTF8;
            byte[] keyStr = encoding.GetBytes(keyVal);
            var hmacSha384 = new HMACSHA384(keyStr);
            return HashAlgorithmBase(hmacSha384, value, encoding);
        }

        /// <summary>
        /// HmacSha512 加密
        /// </summary>
        public static string HmacSha512(this string value, string keyVal)
        {
            ArgumentNullException.ThrowIfNull(value);
            var encoding = Encoding.UTF8;
            byte[] keyStr = encoding.GetBytes(keyVal);
            var hmacSha512 = new HMACSHA512(keyStr);
            return HashAlgorithmBase(hmacSha512, value, encoding);
        }

        /// <summary>
        /// HmacMd5 加密
        /// </summary>
        public static string HmacMd5(this string value, string keyVal)
        {
            ArgumentNullException.ThrowIfNull(value);
            var encoding = Encoding.UTF8;
            byte[] keyStr = encoding.GetBytes(keyVal);
            var hmacMd5 = new HMACMD5(keyStr);
            return HashAlgorithmBase(hmacMd5, value, encoding);
        }

        /// <summary>
        /// HmacRipeMd160 加密
        /// </summary>
        public static string HmacSHA256(this string value, string keyVal)
        {
            ArgumentNullException.ThrowIfNull(value);
            var encoding = Encoding.UTF8;
            //byte[] keyStr = encoding.GetBytes(keyVal);
            // HMACRIPEMD160 hmacRipeMd160 = new HMACRIPEMD160(keyStr);
            var HmacSha256 = new HMACSHA256(encoding.GetBytes(keyVal));
            return HashAlgorithmBase(HmacSha256, value, encoding);
        }

        #endregion

        #region AES 加密解密

        /// <summary>  
        /// AES加密  
        /// </summary>  
        /// <param name="value">待加密字段</param>  
        /// <param name="keyVal">密钥值(最短：16位长度，过长也只取16位)</param>  
        /// <param name="ivVal">加密辅助向量(最短：16位长度，过长也只取16位)</param> 
        /// <returns></returns>  
        public static string AesStr(this string value, string keyVal, string ivVal)
        {
            ArgumentNullException.ThrowIfNull(value);

            var encoding = Encoding.UTF8;
            byte[] btKey = keyVal.FormatByte(encoding);
            byte[] btIv = ivVal.FormatByte(encoding);
            byte[] byteArray = encoding.GetBytes(value);
            string encrypt;
            Aes aes = Aes.Create();
            //Rijndael aes = Rijndael.Create();
            using (var mStream = new MemoryStream())
            {
                using var cStream = new CryptoStream(mStream, aes.CreateEncryptor(btKey, btIv), CryptoStreamMode.Write);
                cStream.Write(byteArray, 0, byteArray.Length);
                cStream.FlushFinalBlock();
                encrypt = Convert.ToBase64String(mStream.ToArray());
            }
            aes.Dispose();
            return encrypt;
        }

        /// <summary>  
        /// AES解密  
        /// </summary>  
        /// <param name="value">待解密字段</param>  
        /// <param name="keyVal">密钥值(最短：16位长度，过长也只取16位)</param>  
        /// <param name="ivVal">加密辅助向量(最短：16位长度，过长也只取16位)</param> 
        /// <returns></returns>  
        public static string UnAesStr(this string value, string keyVal, string ivVal)
        {
            var encoding = Encoding.UTF8;
            byte[] btKey = keyVal.FormatByte(encoding);
            byte[] btIv = ivVal.FormatByte(encoding);
            byte[] byteArray = Convert.FromBase64String(value);
            string decrypt;
            Aes aes = Aes.Create();
            using (var mStream = new MemoryStream())
            {
                using var cStream = new CryptoStream(mStream, aes.CreateDecryptor(btKey, btIv), CryptoStreamMode.Write);
                cStream.Write(byteArray, 0, byteArray.Length);
                cStream.FlushFinalBlock();
                decrypt = encoding.GetString(mStream.ToArray());
            }
            aes.Dispose();
            return decrypt;
        }

        /// <summary>  
        /// AES Byte类型 加密  
        /// </summary>  
        /// <param name="data">待加密明文</param>  
        /// <param name="keyVal">密钥值(最短：16位长度，过长也只取16位)</param>  
        /// <param name="ivVal">加密辅助向量(最短：16位长度，过长也只取16位)</param> 
        /// <returns></returns>  
        public static byte[] AesByte(this byte[] data, string keyVal, string ivVal)
        {
            byte[] bKey = new byte[32];
            Array.Copy(Encoding.UTF8.GetBytes(keyVal.PadRight(bKey.Length)), bKey, bKey.Length);
            byte[] bVector = new byte[16];
            Array.Copy(Encoding.UTF8.GetBytes(ivVal.PadRight(bVector.Length)), bVector, bVector.Length);
            byte[] cryptograph;
            Aes aes = Aes.Create();
            try
            {
                using var mStream = new MemoryStream();
                using var cStream = new CryptoStream(mStream, aes.CreateEncryptor(bKey, bVector), CryptoStreamMode.Write);
                cStream.Write(data, 0, data.Length);
                cStream.FlushFinalBlock();
                cryptograph = mStream.ToArray();
            }
            catch
            {
                cryptograph = null!;
            }
            return cryptograph!;
        }

        /// <summary>  
        /// AES Byte类型 解密  
        /// </summary>  
        /// <param name="data">待解密明文</param>  
        /// <param name="keyVal">密钥值(最短：16位长度，过长也只取16位)</param>  
        /// <param name="ivVal">加密辅助向量(最短：16位长度，过长也只取16位)</param> 
        /// <returns></returns>  
        public static byte[] UnAesByte(this byte[] data, string keyVal, string ivVal)
        {
            byte[] bKey = new byte[32];
            Array.Copy(Encoding.UTF8.GetBytes(keyVal.PadRight(bKey.Length)), bKey, bKey.Length);
            byte[] bVector = new byte[16];
            Array.Copy(Encoding.UTF8.GetBytes(ivVal.PadRight(bVector.Length)), bVector, bVector.Length);
            byte[] original;
            Aes aes = Aes.Create();
            try
            {
                using var mStream = new MemoryStream(data);
                using var cStream = new CryptoStream(mStream, aes.CreateDecryptor(bKey, bVector), CryptoStreamMode.Read);
                using var originalMemory = new MemoryStream();
                byte[] buffer = new byte[1024];
                int readBytes;
                while ((readBytes = cStream.Read(buffer, 0, buffer.Length)) > 0)
                {
                    originalMemory.Write(buffer, 0, readBytes);
                }

                original = originalMemory.ToArray();
            }
            catch
            {
                original = [];
            }
            return original;
        }

        #endregion

        #region DES 加密解密

        /// <summary>
        /// Des加密
        /// </summary>
        /// <param name="value">待加密字符串</param>
        /// <param name="keyVal">密钥值（最长：8位长度）</param>
        /// <param name="ivVal">加密辅助向量（最长：8位长度）</param>
        /// <returns></returns>
        public static string Des(this string value, string keyVal, string ivVal)
        {
            try
            {
                byte[] data = Encoding.UTF8.GetBytes(value);
                //var des = new DESCryptoServiceProvider { Key = Encoding.ASCII.GetBytes(keyVal.Length > 8 ? keyVal.Substring(0, 8) : keyVal), IV = Encoding.ASCII.GetBytes(ivVal.Length > 8 ? ivVal.Substring(0, 8) : ivVal) };
                var des = TripleDES.Create();
                ICryptoTransform transform = des.CreateDecryptor(Encoding.UTF8.GetBytes(keyVal.Length > 8 ? keyVal[..8] : keyVal), Encoding.UTF8.GetBytes(ivVal.Length > 8 ? ivVal[..8] : ivVal));
                byte[] result = transform.TransformFinalBlock(data, 0, data.Length);
                return BitConverter.ToString(result);
            }
            catch (Exception) { return "转换出错！"; }
        }

        /// <summary>
        /// Des解密
        /// </summary>
        /// <param name="value">待加密字符串</param>
        /// <param name="keyVal">密钥值（最长：8位长度）</param>
        /// <param name="ivVal">加密辅助向量（最长：8位长度）</param>
        /// <returns></returns>
        public static string UnDes(this string value, string keyVal, string ivVal)
        {
            try
            {
                string[] sInput = value.Split("-".ToCharArray());
                byte[] data = new byte[sInput.Length];
                for (int i = 0; i < sInput.Length; i++)
                {
                    data[i] = byte.Parse(sInput[i], System.Globalization.NumberStyles.HexNumber);
                }
                // var des = new DESCryptoServiceProvider { Key = Encoding.ASCII.GetBytes(keyVal.Length > 8 ? keyVal.Substring(0, 8) : keyVal), IV = Encoding.ASCII.GetBytes(ivVal.Length > 8 ? ivVal.Substring(0, 8) : ivVal) };
                var des = TripleDES.Create();
                var desencrypt = des.CreateDecryptor(Encoding.UTF8.GetBytes(keyVal), Encoding.UTF8.GetBytes(ivVal));
                byte[] result = desencrypt.TransformFinalBlock(data, 0, data.Length);
                return Encoding.UTF8.GetString(result);
            }
            catch { return "解密出错！"; }
        }

        #endregion

        #region BASE64 加密解密

        /// <summary>
        /// BASE64 加密
        /// </summary>
        /// <param name="value">待加密字段</param>
        /// <returns></returns>
        public static string Base64(this string value)
        {
            var btArray = Encoding.UTF8.GetBytes(value);
            return Convert.ToBase64String(btArray, 0, btArray.Length);
        }

        /// <summary>
        /// BASE64 解密
        /// </summary>
        /// <param name="value">待解密字段</param>
        /// <returns></returns>
        public static string UnBase64(this string value)
        {
            var btArray = Convert.FromBase64String(value);
            return Encoding.UTF8.GetString(btArray);
        }

        #endregion

        #region Base64加密解密
        /// <summary>
        /// Base64加密 可逆
        /// </summary>
        /// <param name="value">待加密文本</param>
        /// <returns></returns>
        public static string Base64Encrypt(string value, Encoding encode = null!)
        {
            if (encode.IsNull()) encode = Encoding.UTF8;
            return Convert.ToBase64String(encode.GetBytes(value));
        }

        /// <summary>
        /// Base64解密
        /// </summary>
        /// <param name="ciphervalue">密文</param>
        /// <returns></returns>
        public static string Base64Decrypt(string ciphervalue, Encoding encode = null!)
        {
            if (encode.IsNull()) encode = Encoding.UTF8;
            return encode.GetString(Convert.FromBase64String(ciphervalue));
        }
        #endregion

        #region 内部方法


        /// <summary>
        /// 转换成字符串
        /// </summary>
        public static string Bytes2Str(this IEnumerable<byte> source, string formatStr = "{0:X2}")
        {
            var pwd = new StringBuilder();
            foreach (byte btStr in source) { pwd.AppendFormat(formatStr, btStr); }
            return pwd.ToString();
        }

        private static byte[] FormatByte(this string strVal, Encoding encoding)
        {
            return encoding.GetBytes(strVal.Base64()[..16].ToUpper());
        }

        /// <summary>
        /// HashAlgorithm 加密统一方法
        /// </summary>
        private static string HashAlgorithmBase(HashAlgorithm hashAlgorithmObj, string source, Encoding encoding)
        {
            byte[] btStr = encoding.GetBytes(source);
            byte[] hashStr = hashAlgorithmObj.ComputeHash(btStr);
            return hashStr.Bytes2Str();
        }

        #endregion

        /// <summary>
        /// 
        /// </summary>
        /// <param name="certPath"></param>
        /// <param name="mchIdPwd"></param>
        /// <returns></returns>
        private static RSA GetPrivateKey(string certPath, string keyPwd)
        {
            //var pc = new X509Certificate2(certPath, keyPwd, X509KeyStorageFlags.Exportable);
            var pc = new X509Certificate2(certPath, keyPwd, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.UserKeySet | X509KeyStorageFlags.MachineKeySet);
            return pc.GetRSAPrivateKey()!;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="message">消息</param>
        /// <param name="certPath">证书路径</param>
        /// <param name="keyPwd">密码</param>
        /// <returns></returns>
        public static string Sha256WithRSA(string message, string certPath, string keyPwd)
        {
            var rsa = GetPrivateKey(certPath, keyPwd);

            var res = new RSACryptoServiceProvider();
            res.ImportParameters(rsa.ExportParameters(true));
            byte[] data = Encoding.UTF8.GetBytes(message);
            byte[] signature = res.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);

            return Convert.ToBase64String(signature);
        }

    }
}
